﻿using System;
using Microsoft.Win32;
using System.Security.Permissions;
using System.Reflection;
using System.Runtime.InteropServices;
using Player;

namespace Centrify.Cfw
{
    public enum RegistryType
    {
        Key32,
        Key64
    }

    public static class RegistryReader
    {
        private static class NativeMethods
        {
            [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern int RegOpenKeyEx(IntPtr hKey,
                                                  string lpSubKey,
                                                  uint ulOptions,
                                                  int samDesired,
                                                  ref IntPtr phkResult);

            public const int HKEY_LOCAL_MACHINE = unchecked((int)0x80000002);

            public const int KEY_QUERY_VALUE = 0x01;
            public const int KEY_WOW64_64KEY = 0x100;
            public const int KEY_WOW64_32KEY = 0x200;
        }

        [SecurityPermissionAttribute(SecurityAction.Demand, UnmanagedCode = true)]
        public static RegistryKey GetRegistryKey(RegistryType type, string keyname)
        {
            RegistryKey regKey = null;
            IntPtr keyHandle = IntPtr.Zero;
            if (NativeMethods.RegOpenKeyEx(new IntPtr(NativeMethods.HKEY_LOCAL_MACHINE),
                                            keyname,
                                            0,
                                            NativeMethods.KEY_QUERY_VALUE | ((type == RegistryType.Key32) ? NativeMethods.KEY_WOW64_32KEY : NativeMethods.KEY_WOW64_64KEY),
                                            ref keyHandle) != 0)
            {
                int error = Marshal.GetLastWin32Error();
                Log.Error("Error occurs when getting registry key {0}, type={1}, error={2}", keyname, type, error);
            }

            if (keyHandle != IntPtr.Zero)
            {
                Log.Verbose("Opened registry key={0}, type={1}", keyname, type);

                // use reflection to get the constuctor of SafeRegistryHandle
                Type safeType = Type.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");
                ConstructorInfo cstr = safeType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
                                                                    null,
                                                                    new Type[] { typeof(IntPtr), typeof(bool) },
                                                                    null);

                // invoke the constuctor to create a SafeRegistryHandle object for the key
                object safeHandle = cstr.Invoke(new object[] { keyHandle, true });

                // use reflection to get the constuctor of RegistryKey
                Type regType = Type.GetType("Microsoft.Win32.RegistryKey");
                cstr = regType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
                                                    null,
                                                    new Type[] { safeType, typeof(bool) },
                                                    null);

                // invoke the constructor to create a RegistryKey object
                regKey = cstr.Invoke(new object[] { safeHandle, true }) as RegistryKey;
            }
            return regKey;
        }

        static public string GetMachineString(string name, string value, bool policy, string defval)
        {
            object ret = GetValue(Registry.LocalMachine, name, value, policy);
            if (ret is String)
                return (string)ret;
            else
                return defval;
        }

        static public Int32 GetMachineInt(string name, string value, bool policy, int defval)
        {
            object ret = GetValue(Registry.LocalMachine, name, value, policy);
            if (ret is Int32)
                return (int)ret;
            else
                return defval;
        }

        static public string[] GetMachineMultiString(string name, string value, bool policy, string[] defval)
        {
            object ret = GetValue(Registry.LocalMachine, name, value, policy);
            if (ret is string[])
                return (string[])ret;
            else
                return defval;
        }

        static public byte[] GetMachineBinary(string name, string value, bool policy, byte[] defval)
        {
            object ret = GetValue(Registry.LocalMachine, name, value, policy);
            if (ret is byte[])
                return (byte[])ret;
            else
                return defval;
        }

        static public bool GetMachineBool(string name, string value, bool policy, bool defval)
        {
            object ret = GetValue(Registry.LocalMachine, name, value, policy);
            if (ret is int)
                return (int)ret != 0;
            else
                return defval;
        }

        static public object GetMachineObject(string name, string value, bool policy, object defval)
        {
            object ret = GetValue(Registry.LocalMachine, name, value, policy);
            return ret ?? defval;
        }

        static public string GetUserString(string name, string value, bool policy, string defval)
        {
            object ret = GetValue(Registry.CurrentUser, name, value, policy);
            if (ret is String)
                return (string)ret;
            else
                return defval;
        }


        static public Int32 GetUserInt(string name, string value, bool policy, int defval)
        {
            object ret = GetValue(Registry.CurrentUser, name, value, policy);
            if (ret is Int32)
                return (int)ret;
            else
                return defval;
        }

        static public string[] GetUserMultiString(string name, string value, bool policy, string[] defval)
        {
            object ret = GetValue(Registry.CurrentUser, name, value, policy);
            if (ret is string[])
                return (string[])ret;
            else
                return defval;
        }

        static public bool GetUserBool(string name, string value, bool policy, bool defval)
        {
            object ret = GetValue(Registry.CurrentUser, name, value, policy);
            if (ret is int)
                return (int)ret != 0;
            else
                return defval;
        }

        static public object GetPolicyObject(RegistryKey basekey, string name, string value, bool policy)
        {
            object retval = null;
            string pkey = CreateKeyName(name, policy);
            RegistryKey polKey = basekey.OpenSubKey(pkey);
            if (polKey != null)
            {
                retval = polKey.GetValue(value);
            }
            return retval;
        }

        static public bool MachineKeyExists(string name)
        {
            string pkey = CreateKeyName(name, false);
            RegistryKey polKey = Registry.LocalMachine.OpenSubKey(pkey);
            return polKey != null;
        }

        static string CreateKeyName(string name, bool policy)
        {
            string pkey = String.Format(@"Software\{0}{1}\{2}", policy ? @"Policies\" : string.Empty, CentrifyCommonSettings.COMPANY_NAME, name);
            return pkey;
        }

        static object GetValue(RegistryKey basekey, string name, string value, bool policy)
        {
            object retval = null;
            if (policy)
            {
                retval = GetPolicyObject(basekey, name, value, true);
            }
            if (retval == null)
            {
                retval = GetPolicyObject(basekey, name, value, false);
            }
            return retval;
        }

    }
}